/** \file wtf2007.cpp
 * Woosta Table Football Engine
 *
 * License... etc.
 **
 * Authors: Dan Horgan, Mac Duy Hai, Sean Micklethwaite
 */

#include "net_asio.h"
#include "3d.h"
#include "3dx.h"
#include "crowd_dynamics.h"
#include <GL/gl.h>
#include <GL/glu.h>
#include "glsl.h"
#include "graphics.h"
#include "gui.h"
#include "gui_console.h"
#include "gui_tests.h"
#include "input.h"
#include <iostream>
#include "loggable.h"
#include "physics_tests.h"
#include "render.h"
#include "scripting.h"
#include "sdlmisc.h"
#include "shader.h"
#include "vector.h"
#include "world.h"
#include "wtflogic.h"
#include "world_camera.h"
#include "audio_core.h"
#include "terrain.h"
#include "fx.h"
#include "pmodel.h"
#include "rts.h"
#include "tools_object_transform.h"
#include "dpc.h"
#include "dotxsi.h"
#include "3d_terrain.h"

using namespace WtfEngine;
using namespace WtfGraphics;

tScreenRef			screen;

WtfGui::WindowManager* 		pWm;

// TEST: an oblong
Mesh::tRef testMesh;

class CamSpin : public Animation {
	Camera::tRef	mrCamera;
	tScalar			mAngle;
	tPosition		mStartPos;

	public:
		CamSpin(const Camera::tRef& rCam):
			mrCamera(rCam), mAngle(0) {};

		void Update(tGameTime dt) {
			mrCamera->setPosition((mrCamera->getPosition() + mrCamera->getDirection()) +
				(Orientation3D::Rotation(mrCamera->getUpDirection(), dt)
					* (-mrCamera->getDirection())));
		};
};


FUNCTOR(Exit)
	Kernel::GetInstance().SetSystemStatus(Kernel::SYSTEM_EXITING);
ENDFUNC;

FUNCTOR(ChangeColour)
	Animation * pAnimEnd, *pAnimSt;

	CacheRef<WtfGui::Colour> col("v_gui_fgColour");

	(pAnimEnd = new Interpolator<fVector4D, Functions<fVector4D>::Linear>(col->Value.Value, fVector4D(1.0, 0, 0, 1.0), 1.0))->After
	(new Interpolator<fVector4D, Functions<fVector4D>::Linear>(col->Value.Value, fVector4D(0, 1, 0, 1.0), 1.0))->After
	(pAnimSt = new Interpolator<fVector4D, Functions<fVector4D>::Linear>(col->Value.Value, fVector4D(0, 0, 1, 1.0), 1.0))->After(pAnimEnd);

	pAnimSt->Init();
ENDFUNC;

FUNCTOR(NetMonitor)
	FUNCTOR(Serve)
		SocketDataEvent * pEv = Kernel::GetInstance().GetCurrentEvent<SocketDataEvent>();
		RawPacket<256>::tRef rPkt = new RawPacket<256>();
		std::string s("HTTP/1.1 200 OK\nContent-Type: text/html\nContent-Length: 12\n\nHello world!");

		for(int i = 0; i < s.length(); i++) {
			(*rPkt)[i] = s[i];
		};

		pEv->getSocket()->SendPacket(rPkt);
	ENDFUNC;

	SocketClientConnectEvent * pEv = Kernel::GetInstance().GetCurrentEvent<SocketClientConnectEvent>();
	Kernel::GetInstance().BindEvent(new SocketDataEvent(pEv->getNewSocket()), new Serve());
	pEv->getNewSocket()->Listen();
ENDFUNC;

class WtfGameState : public GameState
{
public:
	WtfGameState()
		: GameState() {};

protected:
	void Initialize() {

		// Export GUI, RTS
		WtfGui::ExportToScripting();
		WtfRts::ExportToPython();
		WtfEngine::ExportFx();
	};

	void Enable() {
		//SDL_WM_GrabInput(SDL_GRAB_ON);
		//SDL_ShowCursor(SDL_DISABLE);
		SDL_EnableKeyRepeat(1, 1);
	};
};

/**
 * Game initialised here
 */
void Wtf::initGame()
{
	// Init SDL
	if ( SDL_Init(SDL_INIT_VIDEO) < 0 )
	{
		fprintf(stderr, "Failed to initialize SDL: %s\n", SDL_GetError());
		exit(1);
	}

	SDL_EnableUNICODE(SDL_ENABLE);

	screen = new WtfGraphics::Screen();
	screen->SetMode(WtfGraphics::GraphicsMode());

	// Create input handler
	Kernel::GetInstance().RunTask(new InputHandler());

	// Create renderer
	Kernel::GetInstance().RunTask(new Renderer(screen));

	// Create core tasks
	Kernel::GetInstance().RunTask(new DpcDispatcher());
	Kernel::GetInstance().RunTask(new GameManager());
	Kernel::GetInstance().RunTask(new ObjectUpdater());
	Kernel::GetInstance().RunTask(new CollisionDetection());
	Kernel::GetInstance().RunTask(new PhysicsEngine());
	
	Kernel::GetInstance().RunTask(new NetworkManager());

	// Scripting
	new ScriptHost();

	// Audio
	Kernel::GetInstance().RunTask(new WtfAudio::AudioMixer());


	/**** Default Settings *****/

	// Colours
	DataManager::GetInstance().Set(new WtfGui::Colour("v_gui_wmBg", fVector4D(0, 0, 0, 1)));
	DataManager::GetInstance().Set(new WtfGui::Colour("v_gui_bgColour", fVector4D(0.61, 0.78, 0.28, 0.6)));
	DataManager::GetInstance().Set(new WtfGui::Colour("v_gui_fgColour", fVector4D(0.8, 0.8, 0.8, 1.0)));
	DataManager::GetInstance().Set(new WtfGui::Colour("v_gui_buttonBg", fVector4D(0.2, 0.2, 0.2, 0)));
	DataManager::GetInstance().Set(new WtfGui::Colour("v_gui_buttonBgHover", fVector4D(0.2, 0.2, 0.2, 1.0)));
	DataManager::GetInstance().Set(new WtfGui::Colour("v_gui_windowBgColour", fVector4D(0.2, 0.2, 0.2, 0.5)));

	// Fonts
	WtfGui::tFont::Set("v_gui_font", "media/fonts/calibri_bold");

	// Backgrounds
	WtfGui::Widget::RoundedCornerBackground::tCreationOptions bgOpts;

	WtfGui::Widget::tBackground::Set("v_gui_bg", new WtfGui::Widget::RoundedCornerBackground(bgOpts));
	
	bgOpts.rColour = WtfGui::Colour::Get("v_gui_windowBgColour");
	WtfGui::Widget::tBackground::Set("v_gui_windowBg", new WtfGui::Widget::RoundedCornerBackground(bgOpts));
	
	bgOpts.rColour = WtfGui::Colour::Get("v_gui_buttonBgHover");
	WtfGui::Widget::tBackground::Set("v_gui_tableHeaderBg", new WtfGui::Widget::ColourBackground(bgOpts));
	WtfGui::Widget::tBackground::Set("v_gui_tableRecordBg", new WtfGui::Widget::NoBackground());


	// Create initial game state
	Kernel::GetInstance().ForkGameState(new WtfGameState());

	// TEST: Network
	ISocket::tRef rSock = new TcpSocket(80);
	Kernel::GetInstance().GetGameState()->BindEvent(new SocketClientConnectEvent(rSock), new NetMonitor());
	//rSock->Listen();

	// TEST
	//Kernel::GetInstance().RunTask(new PhysicsTests::Stacking());


	// TEST
	// testObj = new Wtf::Row();
	//new Wtf::Table();
	//(new Wtf::Ball())->setVelocity(tVector(0, -5, 0));
	//(new Wtf::Ball())->setVelocity(tVector(0.0, -0.000, 0.0));

	PhysicsTests::Projectile::BindMouseClick();
	//PhysicsTests::Particle::TestThem();
	//new PhysicsTests::RayCast();

	// TEST: Loading X Meshes , follow the definition
	//new Wtf::TestMesh();
	//new WtfGraphics::Test::Text();


	// TEST: NeuralNet
	/*
	Rtl::NeuralNet::tRef rNet = new Rtl::NeuralNet();
	tScalar zIn[2], zOut[1];
	rNet->Load("src/ai/neutest.xml");

	zIn[0] = 1.0; zIn[1] = 1.0;
	rNet->Evaluate(zIn, zOut);*/

	// TEST: terrain
	//TerrainHeightMap::Test();
	//(new WaterSurface(WaterSurface::tCreationOptions(), tVector(100, 1, 100), fVector2D(0.001, 0.001)))
	//	->setPosition(tVector(0, 1, 0));
	//WtfGraphics::TestTerrain();

	// TEST: particles
	FxParticleGenerator::tRef			rPGen(new FxParticleGenerator());
	FxParticleGenerator::tInitOptions	pgenOptions;
	pgenOptions.rGeneratorFunc = (new ObjFunctor<FxParticleGenerator, void, const GraphicsData::tCacheRef&>(rPGen,
		&FxParticleGenerator::GenerateParticle))->Bind(new Primitives::Cube(0.5));
	pgenOptions.vDirection = tVector(1, 1, 0).normalize();
	pgenOptions.Speed = 10;
	pgenOptions.Mass = 1;
	pgenOptions.vPosition = tVector(75, 50, 75);

	//rPGen->Init(pgenOptions);

	//WtfRts::Test();
	new Static(Static::tCreationOptions(), new Primitives::Oblong(100, 1, 100), new PhysicalModels::Oblong("", tVector(100, 1, 100)));

	// Need to initialize this camera after the scene.
	PannableCamera::tRef cam(new PannableCamera(PannableCamera::tCreationOptions(), tVector(30,60,80),
					 tVector(-30,-60,-80)));
	cam->BindToKeyboardAndMouse();

	//Camera::tRef cam(new Camera(tVector(0,100,30), tVector(0,0,0), tVector(0, 1, 0)));
	Renderer::GetInstancePtr()->SetCamera(ICamera::tRef((ICamera*)&*cam));
	//(new CamSpin(cam))->Init();
	
	//(new Static(Static::tCreationOptions(), XsiModel::tCacheRef("G:\\projects\\WTF\\wtf-trunk\\media\\models\\Rowing\\RacingShell8.xsi"), new PhysicalModels::Cube("", 1.0)));

	// Load the main script
	ScriptHost::GetInstance().LoadScript("scripts/WtfGame.py");
};


void WtfEngine::processSdlEvents()
{
	// Moved to InputHandler::Run()
};
